/*
 * @(#)CourseEntry.java	11/05/31
 * Copyleft 2011. All rights reversed.
 *
 * This file is part of AverageGrade.
 *
 * AverageGrade is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * AverageGrade is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with AverageGrade. If not, see <http://www.gnu.org/licenses/>.
 */

package net.grade.averagegrade.parse;

import java.util.Date;

/**
 * Represents an extracted course entry from a <code>CourseEntriesParser</code>.
 * A new <code>CourseEntry</code> may be created by using the inner {@link Builder}
 * class. Supported information include: code, name, points, grade and date. Grades
 * are supported through the {@link Grade} enumeration.
 */
public class CourseEntry {

    /**
     * An enumeration for grades supported by <code>CourseEntry</code>.
     * Also includes support for parsing common grade representation.
     */
    public enum Grade {

        /** Represents the grade commonly referred to as '3'. */
        THREE(3),

        /** Represents the grade commonly referred to as '4'. */
        FOUR(4),

        /** Represents the grade commonly referred to as '5'. */
        FIVE(5);

        /** The numerical representation of this grade. */
        private final int grade;

        /**
         * Creates a new <code>Grade</code> with the specified integer
         * as numerical representation of the grade.
         *
         * @param grade The numerical representation of the grade.
         */
        private Grade(final int grade) {
            this.grade = grade;
        }

        /**
         * Gets the numerical representation of this grade.
         *
         * @return The numerical representation of this grade.
         */
        public int getValue() {
            return grade;
        }

        /**
         * Returns the numerical representation of
         * this grade, as a string object.
         *
         * @return The numerical representation of this
         *         grade, as a string object.
         */
        @Override
        public String toString() {
            return String.valueOf(grade);
        }

        /**
         * Tries to parse the specified string as a <code>Grade</code>.
         * If successful, returns the Grade-representation of the string;
         * Otherwise <code>null</code> is returned instead.
         *
         * @param grade The string to try and parse as a <code>Grade</code>.
         * @return The Grade-representation of the string or <code>null</code>.
         */
        public static Grade parseGrade(final String grade) {
            try {
                return parseGrade(Integer.parseInt(grade));
            } catch(final NumberFormatException ignored) { }

            return null;
        }

        /**
         * Tries to parse the specified integer as a <code>Grade</code>.
         * If successful, returns the Grade-representation of the integer;
         * Otherwise <code>null</code> is returned instead.
         *
         * @param grade The integer to try and parse as a <code>Grade</code>.
         * @return The Grade-representation of the integer or <code>null</code>.
         */
        public static Grade parseGrade(final int grade) {
            switch(grade) {
                case 3:
                    return THREE;
                case 4:
                    return FOUR;
                case 5:
                    return FIVE;
                default:
                    return null;
            }
        }
    }

    /**
     * The builder class for <code>CourseEntry</code>. Mandatory information include
     * the number of points for the course, as well as the achieved grade. Optional
     * information (that still should be entered if possible) include the code, name
     * and date for the entry.
     * <p/>
     * To create a <code>CourseEntry</code>, first create a new builder and set the
     * respective available properties. Then, call the <code>build</code> method to
     * create the new instance.
     */
    public static final class Builder {

        /** The number of points for the course. */
        private final double points;

        /** The achieved grade in the course. */
        private final Grade grade;

        /** The course code for the course. */
        private String code = "";

        /** The name of the course. */
        private String name = "";

        /** The date on which the grade was received. */
        private Date date = null;

        /**
         * Creates a new <code>Builder</code> using the specified parameters
         * as the number of points, respective grade for the course entry.
         * <p/>
         * Throws an exception if the points for the course is less then or equal
         * to zero or if the specified grade is <code>null</code>. This is to indicate
         * that an error probably has occurred during parsing.
         *
         * @param points The number of points for the course.
         * @param grade  The achieved grade in the course.
         * @throws IllegalArgumentException If the points for the course <= 0 or if
         *                                  the specified grade is <code>null</code>.
         */
        public Builder(final double points, final Grade grade) throws IllegalArgumentException {
            if(points <= 0)
                throw new IllegalArgumentException("Number of course points must be > 0 (was " + points + ")");
            if(grade == null) throw new IllegalArgumentException("The grade for the course must not be null");

            this.points = points;
            this.grade = grade;
        }

        /**
         * Sets the course code to that of the specified. Default
         * course code is an empty string. If <code>null</code> is
         * specified as parameter the default value will be set instead.
         *
         * @param code The course code for this entry.
         * @return This <code>Builder</code> instance.
         */
        public Builder code(final String code) {
            this.code = (code == null) ? "" : code;
            return this;
        }

        /**
         * Sets the course name to that of the specified. Default
         * course name is an empty string. If <code>null</code> is
         * specified as parameter the default value will be set instead.
         *
         * @param name The course name for this entry.
         * @return This <code>Builder</code> instance.
         */
        public Builder name(final String name) {
            this.name = (name == null) ? "" : name;
            return this;
        }

        /**
         * Sets the course date to that of the specified. Default date
         * is <code>null</code>.
         *
         * @param date The course date for this entry.
         * @return This <code>Builder</code> instance.
         */
        public Builder date(final Date date) {
            this.date = date;
            return this;
        }

        /**
         * Creates a new <code>CourseEntry</code> using the information
         * provided to this Builder.
         *
         * @return A <code>CourseEntry</code> with information from this Builder.
         */
        public CourseEntry build() {
            return new CourseEntry(this);
        }
    }

    /** The course code for this course. */
    private final String code;

    /** The name of this course. */
    private final String name;

    /** The number of points for this course. */
    private final double points;

    /** The achieved grade in this course. */
    private final Grade grade;

    /** The date on which the grade was received. */
    private final Date date;

    /**
     * Creates a new <code>CourseEntry</code> instance using the
     * information provided by the specified <code>Builder</code>.
     *
     * @param builder The <code>Builder</code> with the information
     *                required to create a new instance.
     */
    private CourseEntry(final Builder builder) {
        code = builder.code;
        name = builder.name;
        points = builder.points;
        grade = builder.grade;
        date = builder.date;
    }

    /**
     * Gets the course code for this entry. If no course
     * code is available, an empty string is returned.
     *
     * @return The course code for this entry.
     */
    public String getCode() {
        return code;
    }

    /**
     * Gets the course name for this entry. If no course
     * name is available, an empty string is returned.
     *
     * @return The course name for this entry.
     */
    public String getName() {
        return name;
    }

    /**
     * Gets the number of points for the course in this entry.
     * The returned value will always be a positive value > 0.
     *
     * @return The number of course points for this entry.
     */
    public double getPoints() {
        return points;
    }

    /**
     * Gets the achieved grade for the course in this entry. The
     * returned <code>Grade</code> always matches a entry in
     * {@link Grade} and is never <code>null</code>.
     *
     * @return The achieved grade for the course in this entry.
     */
    public Grade getGrade() {
        return grade;
    }

    /**
     * Gets the date of when the grade for this course was received.
     * If no date is available, <code>null</code> is returned instead.
     *
     * @return The date of when the grade was received or <code>null</code>.
     */
    public Date getDate() {
        return date;
    }
}